package com.karaoke.oksrv

import com.alibaba.druid.pool.DruidDataSource
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.karaoke.oksrv.websocket.AppWebSocket
import com.karaoke.oksrv.websocket.RoomWebSocket
import org.apache.ibatis.session.SqlSessionFactory
import org.mybatis.spring.SqlSessionFactoryBean
import org.mybatis.spring.annotation.MapperScan
import org.springframework.beans.BeansException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.web.support.SpringBootServletInitializer
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.context.event.ApplicationContextEvent
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.web.socket.config.annotation.EnableWebSocket
import org.springframework.web.socket.server.standard.ServerEndpointExporter
import reactor.Environment
import reactor.bus.EventBus
import reactor.bus.selector.Selectors
import springfox.documentation.service.ApiInfo
import springfox.documentation.service.Contact
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.sql.SQLException
import java.util.*
import javax.sql.DataSource
import kotlin.properties.Delegates


@SpringBootApplication
@EnableSwagger2
@EnableCaching
@EnableAutoConfiguration
class NewsrvApplication : ApplicationContextAware,
        SpringBootServletInitializer(),
        ApplicationListener<ApplicationContextEvent>
{
    override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder
    {
        return application.sources(NewsrvApplication::class.java)
    }

    override fun onApplicationEvent(event: ApplicationContextEvent?)
    {
    }

    override fun setApplicationContext(applicationContext: ApplicationContext)
    {
        ac = applicationContext
    }

    companion object
    {
        var ac: ApplicationContext by Delegates.notNull()

        @Synchronized fun getBean(beanName: String): Any
        {
            return ac.getBean(beanName)
        }

        @Throws(BeansException::class)
        @Synchronized fun <T> getBean(requiredType: Class<T>): T
        {
            return ac.getBean(requiredType)
        }
    }

    @Bean
    @Primary
    open fun customObjectMapper(): ObjectMapper
    {
        return ObjectMapper().apply {
            setSerializationInclusion(JsonInclude.Include.NON_NULL)
            configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
        }

    }

    @Bean
    fun customImplementation(): Docket
    {
        return Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
    }


    private fun apiInfo(): ApiInfo
    {
        return ApiInfo("VOD点播系统",
                       "VOD后台API文档",
                       "1.0.0",
                       "http://127.0.0.1",
                       Contact("Allen", "www.oilinux.com", "316920@qq.com"),
                       "版权所有",
                       "http://www.oilinux.com", listOf())

    }
}

@Configuration
@ConfigurationProperties(prefix = "oksrv")
class serverConfig
{
    var serverIp: String = ""
    var domain: String = ""
    var appVersion: String = ""
}

@Configuration
open class DruidDBConfig
{
    @Value("\${spring.datasource.initialSize}")
    val initialSize: Int = 0

    @Value("\${spring.datasource.minIdle}")
    val minIdle: Int = 0

    @Value("\${spring.datasource.maxActive}")
    val maxActive: Int = 0

    @Value("\${spring.datasource.maxWait}")
    val maxWait: Int = 0

    @Value("\${spring.datasource.timeBetweenEvictionRunsMillis}")
    val timeBetweenEvictionRunsMillis: Int = 0

    @Value("\${spring.datasource.minEvictableIdleTimeMillis}")
    val minEvictableIdleTimeMillis: Int = 0

    @Value("\${spring.datasource.validationQuery}")
    val validationQuery: String? = null

    @Value("\${spring.datasource.testWhileIdle}")
    val testWhileIdle: Boolean = false

    @Value("\${spring.datasource.testOnBorrow}")
    val testOnBorrow: Boolean = false

    @Value("\${spring.datasource.testOnReturn}")
    val testOnReturn: Boolean = false

    @Value("\${spring.datasource.poolPreparedStatements}")
    val poolPreparedStatements: Boolean = false

    @Value("\${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
    val maxPoolPreparedStatementPerConnectionSize: Int = 0

    @Value("\${spring.datasource.filters}")
    val filters: String? = null

    @Value("\${spring.datasource.connectionProperties}")
    val connectionProperties: String? = null
}

@Configuration
@MapperScan(basePackages = arrayOf("com.karaoke.oksrv.mapper.emedia"), sqlSessionFactoryRef = "eMediaSqlSessionFactory")
class eMediaDataSourceConfig
{
    @Autowired
    lateinit var druidDBConfig: DruidDBConfig
    @Qualifier("serverConfig") @Autowired lateinit var serverConfig: serverConfig

    @Bean(name = arrayOf("eMediaDataSource"))
    open fun eMediaDataSource(): DataSource
    {
        val datasource = DruidDataSource()

        datasource.url = "jdbc:sqlserver://${serverConfig.serverIp}:1433;databaseName=eMedia"
        datasource.username = "sa"
        datasource.password = "jiandanJHF88"
        datasource.driverClassName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"

        //configuration
        datasource.initialSize = druidDBConfig.initialSize
        datasource.minIdle = druidDBConfig.minIdle
        datasource.maxActive = druidDBConfig.maxActive
        datasource.maxWait = druidDBConfig.maxWait.toLong()
        datasource.timeBetweenEvictionRunsMillis = druidDBConfig.timeBetweenEvictionRunsMillis.toLong()
        datasource.minEvictableIdleTimeMillis = druidDBConfig.minEvictableIdleTimeMillis.toLong()
        datasource.validationQuery = druidDBConfig.validationQuery
        datasource.isTestWhileIdle = druidDBConfig.testWhileIdle
        datasource.isTestOnBorrow = druidDBConfig.testOnBorrow
        datasource.isTestOnReturn = druidDBConfig.testOnReturn
        datasource.isPoolPreparedStatements = druidDBConfig.poolPreparedStatements
        datasource.maxPoolPreparedStatementPerConnectionSize = druidDBConfig.maxPoolPreparedStatementPerConnectionSize
        try
        {
            datasource.setFilters(druidDBConfig.filters)
        }
        catch (e: SQLException)
        {

        }

        datasource.setConnectionProperties(druidDBConfig.connectionProperties)

        return datasource
    }

    @Bean(name = arrayOf("eMediaSqlSessionFactory"))
    @Throws(Exception::class)
    fun eMediaSqlSessionFactory(@Qualifier("eMediaDataSource") dataSource: DataSource): SqlSessionFactory
    {
        val bean = SqlSessionFactoryBean()
        bean.setDataSource(dataSource);
        bean.setMapperLocations(PathMatchingResourcePatternResolver().getResources("classpath:mapper/eMedia/*.xml"))
        return bean.`object`
    }
}

@Configuration
@MapperScan(basePackages = arrayOf("com.karaoke.oksrv.mapper.master"), sqlSessionFactoryRef = "masterSqlSessionFactory")
class mediaDataSourceConfig
{
    @Autowired
    lateinit var druidDBConfig: DruidDBConfig
    @Qualifier("serverConfig") @Autowired lateinit var serverConfig: serverConfig

    @Bean(name = arrayOf("masterDataSource"))
    @Primary
    open fun primaryDataSource(): DataSource
    {
        val datasource = DruidDataSource()

        datasource.url = "jdbc:sqlserver://${serverConfig.serverIp}:1433;databaseName=nok"
        datasource.username = "sa"
        datasource.password = "jiandanJHF88"
        datasource.driverClassName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"

//        datasource.url = "jdbc:postgresql://${serverConfig.serverIp}:5432/nok"
//        datasource.username = "postgres"
//        datasource.password = "jiandan88"
//        datasource.driverClassName = "org.postgresql.Driver"

        //configuration
        datasource.initialSize = druidDBConfig.initialSize
        datasource.minIdle = druidDBConfig.minIdle
        datasource.maxActive = druidDBConfig.maxActive
        datasource.maxWait = druidDBConfig.maxWait.toLong()
        datasource.timeBetweenEvictionRunsMillis = druidDBConfig.timeBetweenEvictionRunsMillis.toLong()
        datasource.minEvictableIdleTimeMillis = druidDBConfig.minEvictableIdleTimeMillis.toLong()
        datasource.validationQuery = druidDBConfig.validationQuery
        datasource.isTestWhileIdle = druidDBConfig.testWhileIdle
        datasource.isTestOnBorrow = druidDBConfig.testOnBorrow
        datasource.isTestOnReturn = druidDBConfig.testOnReturn
        datasource.isPoolPreparedStatements = druidDBConfig.poolPreparedStatements
        datasource.maxPoolPreparedStatementPerConnectionSize = druidDBConfig.maxPoolPreparedStatementPerConnectionSize
        try
        {
            datasource.setFilters(druidDBConfig.filters)
        }
        catch (e: SQLException)
        {

        }

        datasource.setConnectionProperties(druidDBConfig.connectionProperties)

        return datasource
    }

    @Bean(name = arrayOf("masterSqlSessionFactory"))
    @Primary
    @Throws(Exception::class)
    fun mediaSqlSessionFactory(@Qualifier("masterDataSource") dataSource: DataSource): SqlSessionFactory
    {
        val bean = SqlSessionFactoryBean()
        bean.setDataSource(dataSource)
        bean.setMapperLocations(PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"))
        return bean.`object`
    }
}

@Configuration
@EnableWebSocket
open class WebSocketConfig
{
    @Bean
    fun serverEndpointExporter(): ServerEndpointExporter
    {
        return ServerEndpointExporter()
    }

    @Bean
    fun reverseWebSocketEndpoint(): RoomWebSocket
    {
        return RoomWebSocket()
    }

    @Bean(name = arrayOf("webSocketMap"))
    fun webSocketMap(): WeakHashMap<String, RoomWebSocket>
    {
        return WeakHashMap()
    }

    @Bean(name = arrayOf("appSocketMap"))
    fun appSocketMap(): WeakHashMap<String, AppWebSocket> = WeakHashMap()
}

@Configuration
@ConfigurationProperties(prefix = "karaoke.update")
open class UpdateInfo
{
    var baseVersion: String? = null
    var updateVersion: String? = null
    var dexPatchVersion: Long = 0
    var dbVersion: Int = 0
    var apkUrl: String? = null
}


fun main(args: Array<String>)
{
    SpringApplication.run(NewsrvApplication::class.java, *args)
}
